#ifndef TYPES_H
#define TYPES_H

#include <setjmp.h>

#include "unicode/utypes.h"
#include "gmp.h"

#include "symbol-table.h"

union _node;
union _value;

struct _generic_list;

#define NODE_HEADER   \
  type_t type;        \
  struct _generic_list *tags

typedef struct {
  UChar *filename;
  int line;
  int column;
} node_data;

/*
Naming convention:
- 'allocate' creates a value of the appropriate type, performing no
  initialisation beyond setting the 'type' member
- 'init' (better name?) populates the value with file position information
- 'make' takes a set of arguments and populates the value accordingly
  (should this create its own values, or populate existing ones?)
*/

struct token;
struct name_list;

#define SHOW(type) token_names[type]

/*
  Need to distinguish between constructor types and value types.
  Constructor types live purely in the AST and evaluate to something
  other than themselves. Value types are live values upon which
  operations can be performed.

  There's a rough distinction at present, but for lists particularly
  it would be helpful to formalise it:

  e.g. split list into list vs list_cons

  We should also remove auxiliary cruft such as g_l_t from this enum
  and phase out its use for such types within the interpreter.
*/

/*

typedef enum _type {

  // Values
  type_list,
  type_integer,
  type_float,
  type_tag,
  type_character,
  type_string,
  type_reference,
  type_builtin,
  type_scope,
  type_closure,
  type_trampoline,
  type_dictionary?
  type_foreign?
  type_vector?
  type_byte[string]?

  // Constructors
  type_constructor, // Fixed point of reference
  type_name,
  type_list_constructor,
  type_append,
  type_interpolate,
  type_function,
  type_dictionary_constructor?
  type_vector_constructor?

}

*/
/*
typedef enum _type {
  type_list = 1,
  type_integer,
  type_float,
  type_tag,
  type_function,
  type_name,
  type_string,
  type_character,
  // Operators
  type_call,
  type_append,
  type_interpolate,
  type_reference,
  type_builtin,
  type_scope,
  type_closure,
  type_trampoline,
  type_generic_list
} type_t;*/

// Does shifting type_list break things?

typedef enum _type {

  value_type,
  // Value Types

  type_integer,
  type_float,
  type_tag,
  type_character,
  type_string,
  type_closure,
  type_scope,
  type_reference,
  type_builtin,
  type_trampoline,

  constructor_type,
  // Constructor Types

  type_list,
  type_function,
  type_call,
  type_append,
  type_interpolate,
  type_name,

  garbage_type,
  type_generic_list

} type_t;
/*
  [PROPOSAL] Representing value types as tagged pointer
  (Assumes GC returns 2-byte aligned pointers - can we asume more / less?)

  Misaligned pointers may be used to represent inline values to avoid
  the overhead of allocating an object to hold them. This may massively
  reduce memory usage and GC overhead when applied to common types.

  Candidates:
  * single-level names
  * tags
  * 32bit integers
  * [] (the empty list)

  Encoding scheme?

  63:32   31:0
  Payload Description
  ...

  7:0 (type)
  0x01 -> fixnums
  0x05 -> tags
  0x07 -> constants
  0xF1 -> name

*/

#include "generic-list.h"

struct _generic;
struct _list;
struct _integer;
struct _float;
struct _tag;
struct _function;
struct _name;
struct _call;

typedef struct _generic {
  NODE_HEADER;
} generic_t;

typedef struct _integer {
  NODE_HEADER;
  mpz_t value;
} integer_t;

typedef struct _float {
  NODE_HEADER;
  mpf_t value;
} float_t;

typedef union _number {
  integer_t integer;
  float_t floatt;
} number_t;

/*
  Most names will consist of a single 'head' part. This is stored within the
  struct to avoid the cost of following a pointer in the usual case. Where
  the name has only one part, parts is NULL.
*/
typedef struct _name {
  NODE_HEADER;
  symbol_t head;
  generic_list_t *parts;
} name_t;


struct _scope;

typedef struct _function {
  NODE_HEADER;
  // Is there an appropriate boolean type?
  int recursive;
  generic_list_t *arguments;
  name_t *tail;
  generic_list_t *body;
} function_t;

typedef struct _closure {
  NODE_HEADER;
  function_t *function;
  struct _scope *scope;
} closure_t;

typedef struct _call {
  NODE_HEADER;
  UChar *filename;
  unsigned int line_number;
  union _value *head;
  generic_list_t *arguments;
  int in_stack;
} call_t;

typedef struct _tag {
  NODE_HEADER;
  // UChar *value;
  symbol_t value;
} tag_t;

typedef struct _character {
  NODE_HEADER;
  UChar value;
} character_t;

typedef struct _string {
  NODE_HEADER;
  UChar *value;
} string_t;

typedef struct _interpolate {
  NODE_HEADER;
  generic_list_t *values;
} interpolate_t;

typedef union {
  string_t *string;
  interpolate_t *interpolate; 
} string_types_t;

typedef struct _list {
  NODE_HEADER;
  generic_list_t *items;
  int concrete;
  int string;
} list_t;

// TODO: Make these use bit fileds
#define CONCRETE(L) (((list_t*)L)->flags && 1)
#define STRING(L) (((list_t*)L)->flags && 2)

#define SET_CONCRETE(L) (((list_t*)L)->flags |= 1)
#define SET_STRING(L) (((list_t*)L)->flags |= 2)

#define SET_NOT_CONCRETE(L) (((list_t*)L)->flags &= ~1)
#define SET_NOT_STRING(L) (((list_t*)L)->flags &= ~2)

struct _value_list;

typedef struct _append {
  NODE_HEADER;
  generic_list_t *lists;
} append_t;

typedef union {
  append_t append;
  list_t list;
} list_types_t;

typedef struct _reference {
  NODE_HEADER;
  union _value *value;
} reference_t;

struct _scope;
struct _binding;

typedef struct _scope {
  NODE_HEADER;
  // 0 -> builtins
  struct _scope *parent;
  struct _binding *first;
  generic_list_t *uses;
  int clock;
} scope_t;

typedef struct _use {
  int clock;
  scope_t *scope;
} use_t;

struct _handler;

typedef struct _builtin {
  NODE_HEADER;
  union _value *(*function)(generic_list_t*,scope_t*,int,struct _handler*);
  name_t *name;
  int arity;
  int varargs;
} builtin_t;

typedef struct _foreign {
  NODE_HEADER;
  void *value;
} foreign_t;

typedef struct _trampoline {
  NODE_HEADER;
  call_t *call;
  struct _scope *closure;
} trampoline_t;

typedef union _value {
  generic_t generic;
  list_t list;
  integer_t integer;
  // Fix this name, it's dumb
  float_t floatt;
  tag_t tag;
  function_t function;
  name_t name;
  call_t call;
  string_t string;
  character_t character;
  append_t append;
  interpolate_t interpolate;
  builtin_t builtin;
  closure_t closure;
  generic_list_t generic_list;
  trampoline_t trampoline;
  reference_t reference;
} value_t;

typedef struct _binding {
  //name_t *name;
  symbol_t name;
  value_t *value;
  int clock;
  int hidden;
  struct _binding *next;
} binding_t;

typedef struct _handler {
  jmp_buf jump;
  tag_t *tag;
  value_t *value;
  unsigned int status;
  generic_list_t *stack;
  generic_list_t *handling;
} handler_t;


/*
See function definitions for details of preconditions and semantics
*/
value_t *allocate_type(type_t value_type);

int has_type(void *value, type_t type);
type_t get_type(value_t *value);

int is_numeric(value_t *value);
int is_iterable(value_t *value);
int is_callable(value_t *value);

int ordered_types(value_t *a, value_t *b);

int get_arity(value_t *value);
int has_variable_arity(value_t *value);
int has_arity(value_t *value, int arguments);
int is_recursive(value_t *value);

int promote(value_t **a, value_t **b);

float_t *integer_to_float(integer_t *integer);
integer_t *float_to_integer(float_t *floatt);

list_t *string_to_list(string_t *string);

int get_length(value_t *a);

#define is_tagged_pointer(x) (0x1 & ((uintptr_t)(x)))

//uint32_t unpack_pointer_tag(value_t *pointer);
//uint32_t unpack_pointer_payload(value_t *pointer);

#define POINTER_SIZE (sizeof(void*) * 8)
#define unpack_pointer_tag(Pointer) \
  ((uint16_t)(uintptr_t)(Pointer))
#define unpack_pointer_payload(Pointer) \
  ((uintptr_t)(((uintptr_t)(Pointer)) >> (POINTER_SIZE / 2)))

/*AST Types */
name_t *make_name(UChar *string);
name_t *make_lookup(UChar *string);
int name_is_lookup(name_t *name);
append_t *make_append(generic_list_t *lists);

/*Primitive Types */
tag_t *make_tag(UChar *string);
character_t *make_character(UChar character);
string_t *make_string(UChar *string);

integer_t *make_integer(unsigned int integer);
float_t *make_float_str(UChar *string);
integer_t *make_integer_str(UChar *string);

/*Compound Types */
function_t *make_function(generic_list_t *arguments, name_t *tail, generic_list_t *body);
call_t *make_call(value_t *head, generic_list_t *arguments);
list_t *make_list(generic_list_t *values);
int list_empty(value_t *list);
value_t *list_index(value_t *list, int n);

interpolate_t *make_interpolate(generic_list_t *values);

builtin_t *make_builtin(name_t *name, value_t *(*function)(generic_list_t*,scope_t*,int,handler_t*), int arity, int varargs);

closure_t *make_closure(scope_t *scope, function_t *function);
trampoline_t *make_trampoline(call_t *call, scope_t *scope);

reference_t *make_reference(value_t *value);

UChar *to_unicode(char *string);
char *from_unicode(UChar *string);
#endif // TYPES_H
